home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / mm / ccmd / test.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-12-18  |  26.1 KB  |  1,033 lines

  1. /*
  2.  Copyright (c) 1986, 1990 by The Trustees of Columbia University in
  3.  the City of New York.  Permission is granted to any individual or
  4.  institution to use, copy, or redistribute this software so long as it
  5.  is not sold for profit, provided this copyright notice is retained.
  6.  
  7.  Author: Andy Lowry ; Rewritten by Howie Kaye
  8. */
  9.  
  10. /*
  11.  * The purpose of this program is to demonstrate some of the features of
  12.  * CCMD.  It is also a way of testing the various functions of the
  13.  * interface.  Hopefully it will be instrumental in defining a style for
  14.  * writing code to use CCMD.
  15.  *
  16.  * There are certain issues addressed in several of these routines which
  17.  * are of note.
  18.  * 1) the value returned from a parse MUST be copied before the next parse.
  19.  *    The next parse will invalidate the parseval structure used.
  20.  *    This does not mean that the data pointed to becomes invalid.  Only the
  21.  *    pointer (note that for file parses, the data pointed to is reused by
  22.  *    the next file parse)
  23.  * 2) due to reparsing, multiple calls to malloc() may be done.  It is
  24.  *    important to free these pointers before the next malloc().  This is
  25.  *    similar to the problem with JFNs under COMND% in TOPS20.
  26.  */
  27.  
  28. #include "ccmd.h"
  29.  
  30. int debug = 0;
  31. /*
  32.  * forward declare parse routines.
  33.  */
  34. int octnum(), decnum(), hexnum(), key(), qst(), dst(), b5num(), exitcmd();
  35. int text(), word(), swit(), tok(), fil(), tim(), tad(), dat(), user();
  36. int groupcmd(),para(),contpara(),take(),comment(), password();
  37. int domatch(), help(), character(), shell(), nil(), history();
  38.  
  39. char *malloc();
  40. static char *atmbuf;
  41. /*
  42.  * set up the top level keyword table
  43.  * This will be used by the help command also.
  44.  */
  45.  
  46. static keywrd cmds[] = {
  47.   { "base-5",    0,    (keyval) b5num },
  48.   { "character",0,    (keyval) character },
  49.   { "comment",    0,    (keyval) comment },
  50.   { "continue",    0,    (keyval) contpara },
  51.   { "date",    0,    (keyval) dat },
  52.   { "decimal",    0,    (keyval) decnum },
  53.   { "delimited",0,    (keyval) dst },
  54.   { "exit",    0,    (keyval) exitcmd },
  55.   { "file",    0,    (keyval) fil },
  56.   { "group",    0,    (keyval) groupcmd },
  57.   { "h",     KEY_ABR|KEY_INV, (keyval) 11 },    /* abbrev for help -- index == 11 */
  58.   { "help",    0,    (keyval) help },
  59.   { "hexadecimal",0,    (keyval) hexnum },
  60.   { "history",  0,     (keyval) history },
  61.   { "line",    0,    (keyval) text },    
  62.   { "match",    0,    (keyval) domatch },
  63.   { "octal",    0,    (keyval) octnum },
  64.   { "paragraph",0,    (keyval) para },
  65.   { "password",    0,    (keyval) password },
  66.   { "person",    0,    (keyval) key },
  67.   { "q",     KEY_ABR|KEY_INV, (keyval) 21 },    /* abbrev for quit -- index == 12 */
  68.   { "quit",    KEY_INV,(keyval) exitcmd },
  69.   { "quoted",    0,    (keyval) qst },
  70.   { "switch",    0,    (keyval) swit },
  71.   { "tad",    0,    (keyval) tad },
  72.   { "take",    0,    (keyval) take },
  73.   { "time",    0,    (keyval) tim },
  74.   { "token",    0,    (keyval) tok },
  75.   { "user",    0,    (keyval) user },
  76.   { "word",    0,    (keyval) word },
  77.   { "shell",    0,    (keyval) shell },
  78.   { "nil",     KEY_INV,(keyval) nil },
  79. };
  80.  
  81. /*
  82.  * the actual keyword table type has a length in it.
  83.  */
  84. static keytab cmdtab = { (sizeof(cmds)/sizeof(keywrd)), cmds };
  85.  
  86. pval parseval;
  87. fdb *used;
  88.  
  89.  
  90. int argc;
  91. char *argv;
  92. main(Argc,Argv) int Argc; char *Argv;
  93. {
  94.   char *cmini();
  95.   argc = Argc;
  96.   argv = Argv;
  97.   atmbuf = cmini();            /* initialize ccmd */
  98.   if (!(cmcsb._cmflg & CM_ITTY) && (cmcsb._cmflg & CM_OTTY))
  99.                     /* not reading from a tty */
  100.       cmseti(stdin, NULL, stderr);    /* so don't echo */
  101.  
  102.   cmhst(100);
  103.   cmxprintf("Test program Version 0.0\n"); /* display version info */
  104.   cmxprintf("%s\n",cm_version());    /* and CCMD version as well. */
  105.   toplevel();                /* top level cmd parser. */
  106.   cmdone();                /* restore terminal, etc */
  107.   return(0);
  108. }
  109.  
  110. int done = FALSE;            /* exit flag */
  111. toplevel() {
  112.                     /* FDB for top level commands */
  113.   static fdb cmdfdb = { _CMKEY, 0, NULL, (pdat) &(cmdtab), "Command, ", 
  114.               NULL, NULL };
  115.   extern int cmerjnp();
  116.   while (!done) {
  117.     cmseter();                /* set error trap */
  118.     cmcsb._cmerh = cmerjnp;        /* handler shouldn't print message */
  119.  
  120.                     /* control will come here in the */
  121.                     /* case of a parse error. */
  122.     if (cmcsb._cmerr == CMxEOF)        /* exit on EOF -- this is useful */
  123.       break;                /* for take's and ^D EOF's */
  124.     if (cmcsb._cmerr != CMxOK) {    /* nonEOF error -- print msg */
  125.     cmperr(cmcsb._cmerr,0);
  126.     cmcsb._cmerr = CMxOK;        /* and reset error condition */
  127.     }
  128.     if (cmargs(argc,argv))        /* check for command line args */
  129.       done = TRUE;
  130.     else
  131.       prompt("Test> ");            /* prompt */
  132.     cmsetrp();                /* set reparse trap */
  133.                     /* control will come here in the */
  134.                     /* case of a reparse. */
  135.     parse(&cmdfdb,&parseval,&used);    /* parse a command */
  136.     execute(parseval._pvkey);        /* execute it. */
  137.   }
  138. }
  139.  
  140. /*
  141.  * execute a command.
  142.  * call the function, with the help flag off.
  143.  */
  144. execute(f) int (*f)(); {
  145.   (*f)(FALSE);                /* call function with help flag off */
  146. }
  147.  
  148. /*
  149.  * call a command function with the help flag on
  150.  */
  151. dohelp(f) int (*f)(); {
  152.   (*f)(TRUE);                /* call function with help flag on */
  153. }
  154.  
  155.  
  156. /*
  157.  * top level commands.
  158.  * all of these take the form 
  159.  * command(helpflag) {
  160.  *   if (helpflag) print a help string
  161.  *   else { parse and execute }
  162.  *
  163.  * this style is encouraged, as the documentation for each command
  164.  * resides inside the command itself.  This leads to self documenting
  165.  * parses, and documentation which is consistant with the code.
  166.  */
  167.  
  168. key(helpflg) int helpflg; {
  169. /*
  170.  * keyword table for the name command
  171.  */
  172.   static keywrd keys[] = {
  173.     { "andy", 0, (keyval) 1 },
  174.     { "and", KEY_NOR+KEY_INV, (keyval) 2}, /* this essentially turns off esc */
  175.                     /* completion on andy. */
  176.     { "dee", 0, (keyval) 3 },
  177.     { "doug", 0, (keyval) 4 },
  178.     { "d", KEY_ABR+KEY_INV, (keyval) 3 },
  179.     { "frank", KEY_INV, (keyval) 6 },    /* frank is invisible */
  180.     { "howie", 0, (keyval) 7 },
  181.     { "how", KEY_NOR+KEY_INV, (keyval) 8 },/*complete on howie after the 'i' */
  182.     { "mark", 0, (keyval) 9 },
  183.     { "tom.chow", 0, (keyval) 10 }    /* has a dot in it. */
  184.   };
  185.  
  186.   static keytab ktab = { (sizeof(keys)/sizeof(keywrd)), keys, 30 };
  187.  
  188. /* 
  189.  * custom break table -- like the standard for keywords, but allows
  190.  * periods in other than the first position.
  191.  */
  192.  
  193.   static brktab keybrk = {
  194.     {                    /* 1st char break array */
  195.                           /* all but letters, digits, hyphen */
  196.       0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0x00, 0x3f, 
  197.       0x80, 0x00, 0x00, 0x1f, 0x80, 0x00, 0x00, 0x1f
  198.     },
  199.     {                    /* subsequent char break array */
  200.                     /* same as above, plus dots */
  201.       0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0x00, 0x3f, 
  202.       0x80, 0x00, 0x00, 0x1f, 0x80, 0x00, 0x00, 0x1f
  203.     }
  204.   };
  205.     
  206.                     /* fdb for names.   default to dee */
  207.   static fdb keyfdb = { _CMKEY, KEY_WID, NULL, (pdat) &ktab, "Name, ", "dee",
  208.             &keybrk };
  209.  
  210.   static int choice;
  211.  
  212. /*
  213.  * at last, some code.
  214.  */
  215.   if (helpflg) {
  216.     cmxprintf("\
  217. The key command parses a bunch of keywords.  Actually, these are mostly\n\
  218. people's names.  There are a few ignored keywords, which prevent escape\n\
  219. completion on others.\n");
  220.   }
  221.   else {
  222.     noise("of choice is");        /* give a guide word */
  223.     parse(&keyfdb,&parseval,&used);    /* parse a keyword */
  224.     choice = parseval._pvkey;        /* remember which was chosen */
  225.     confirm();                /* confirm the request */
  226.     cmxprintf("Selected name: %s\n",keys[choice-1]._kwkwd);
  227.   }
  228. }
  229.  
  230. /*
  231.  * the switch command
  232.  */
  233.  
  234. swit(helpflg)
  235. int helpflg;
  236. {
  237.   static keywrd swits[] = {
  238.     { "find:", KEY_INV, (keyval) 0 },    /* invisible, takes a value */
  239.                     /* prevent completion on hello-world */
  240.     { "hell", KEY_NOR+KEY_INV, (keyval) 1 },
  241.     { "hello-world", 0, (keyval) 2 },
  242.     { "junk", 0, (keyval) 3 },
  243.     { "old", 0, (keyval) 4 },
  244.     { "qui", KEY_NOR+KEY_INV, (keyval) 5},
  245.     { "quiet", 0, (keyval) 6 },
  246.     { "s", KEY_ABR+KEY_INV, (keyval) 8 },
  247.     { "stuff:", 0, (keyval) 8 },    /* takes a value */
  248.     { "stupid", 0, (keyval) 9 },
  249.     { "time", 0, (keyval) 10 },
  250.   };
  251.  
  252.   static keytab stab = { (sizeof(swits)/sizeof(keywrd)), swits };
  253.   static fdb swifdb = { _CMSWI, 0, NULL, (pdat) &stab, NULL, "/time", NULL };
  254.   static int choice, value, valflg;
  255.   static fdb valfdb = { _CMNUM, 0, NULL, (pdat) 10, NULL, NULL, NULL };
  256.  
  257.   if (helpflg) {
  258.     cmxprintf("\
  259. The switch command parses a bunch of switches.  Switches are keywords which\n\
  260. begin with a switch character.  In this case, the switch character is '/'.\n\
  261. It is possible to change the switch character to any other character by \n\
  262. changing the 'cmswbeg' variable in ccmd.  You can do this with in this \n\
  263. program by using the 'character' command.  Type 'help character' for more\n\
  264. information about it.\n\
  265. \n\
  266. A switch may also take a value after it.  If the switch ends with a \n\
  267. colon ':', then it accepts another (integer) value.\n");
  268.   }
  269.   else {
  270.     noise("selection is");        /* some extra help */
  271.     parse(&swifdb,&parseval,&used);    /* parse the switch */
  272.     choice = parseval._pvkey;        /* remember which */
  273.     if (cmcsb._cmflg & CM_SWT) {    /* if the switch takes a value */
  274.       parse(&valfdb,&parseval,&used);    /*  parse  that too.*/
  275.       value = parseval._pvint;
  276.       valflg = TRUE;
  277.     }
  278.     else
  279.       valflg = FALSE;
  280.   }
  281.   confirm();                /* get comfirmation */
  282.   cmxprintf("Selected switch: %s\n",swits[choice]._kwkwd);
  283.   if (valflg)
  284.     cmxprintf("Switch value: %d\n",value);
  285. }
  286.  
  287. /*
  288.  * parse a hex number
  289.  */
  290. hexnum(helpflg)
  291. int helpflg;
  292. {
  293.   static int value;
  294.   static fdb hexfdb = { _CMNUM, NUM_US, NULL, (pdat) 16, NULL, NULL, NULL };
  295.  
  296.   if (helpflg)
  297.     cmxprintf("\
  298. The hexnum command accepts a Hexadecimal number, and displays it.  the\n\
  299. number is actually returned in decimal, and printed out again in \n\
  300. hexadecimal.\n");
  301.   else {
  302.     noise("number is");
  303.     parse(&hexfdb,&parseval,&used);
  304.     value = parseval._pvint;
  305.     confirm();
  306.     cmxprintf("Value: %x\n", value);
  307.   }
  308. }
  309.  
  310. /*
  311.  * parse a decimal number
  312.  */
  313. decnum(helpflg)
  314. int helpflg;
  315. {
  316.   static int value;
  317.   static fdb decfdb = { _CMNUM, 0, NULL, (pdat) 10, NULL, NULL, NULL };
  318.  
  319.   if (helpflg)
  320.     cmxprintf("\
  321. The decimal command accepts a decimal number, and displays it.\n");
  322.   else {
  323.     noise("number is");
  324.     parse(&decfdb,&parseval,&used);
  325.     value = parseval._pvint;
  326.     confirm();
  327.     cmxprintf("Value: %d\n", value);
  328.   }
  329. }
  330.  
  331. /*
  332.  * parse an octal number
  333.  */
  334. octnum(helpflg)
  335. int helpflg;
  336. {
  337.   static char *value = NULL;
  338.   static int octdat = 8;
  339.   static fdb octfdb = { _CMNUM, NUM_PO, NULL, (pdat) 8, NULL, NULL, NULL };
  340.  
  341.   if (helpflg) {
  342.     cmxprintf("The octal command accepts an octal number.\n");
  343.   }
  344.   else {
  345.     noise("number is");
  346.     parse(&octfdb,&parseval,&used);    /* parse an octal number */
  347.     if (value) {
  348.       free(value);
  349.       value = NULL;
  350.     }
  351.     value = malloc(strlen(atmbuf)+1);    /* get the string, not the number */
  352.     strcpy(value,atmbuf);        /* because printf doesn't support */
  353.     confirm();                /* the printing of octal numbers */
  354.     cmxprintf("Value: %s\n", value);
  355.     free(value);
  356.     value = NULL;
  357.   }
  358. }
  359.  
  360. /*
  361.  * a base five number
  362.  */
  363. b5num(helpflg)
  364. int helpflg;
  365. {
  366.   static int value;
  367.   static int b5dat = 5;
  368.   static fdb b5fdb = { _CMNUM, 0, NULL, (pdat) 5, NULL, NULL, NULL };
  369.  
  370.   if (helpflg)
  371.     cmxprintf("accepts a number in base 5, and displays it in decimal.\n");
  372.   else {
  373.     noise("number is");
  374.     parse(&b5fdb,&parseval,&used);
  375.     value = parseval._pvint;        /* get the number in decimal */
  376.     confirm();
  377.     cmxprintf("Value (decimal): %d\n", value); /* print it */
  378.   }
  379. }
  380.  
  381. /*
  382.  * the exit command
  383.  */
  384. exitcmd(helpflg)
  385. int helpflg;
  386. {
  387.   if (helpflg) {
  388.     cmxprintf("\
  389. The exit command exits from this program.  The invisible quit command is\n\
  390. a synonym for it.\n");
  391.   }
  392.   else {
  393.     noise("this goofy program");
  394.     confirm();
  395.     done = TRUE;            /* just set the global exitflg */
  396.   }
  397. }
  398.  
  399. /*
  400.  * parse a quoted string.
  401.  * quoted strings must be quoted by '"'.  They are a specific case of
  402.  * delimited strings.
  403.  */
  404.  
  405. qst(helpflg)
  406. int helpflg;
  407. {
  408.   static fdb qstfdb = { _CMQST, 0, NULL, NULL, NULL, NULL, NULL };
  409.   static char *valstr= NULL;
  410.   
  411.   if (helpflg) {
  412.     cmxprintf("Accepts a quoted string.\n");
  413.   }
  414.   else {
  415.     noise("string is");
  416.     parse(&qstfdb,&parseval,&used);    /* parse a string */
  417.     if (valstr) {
  418.       free(valstr);
  419.       valstr = NULL;
  420.     }
  421.     valstr = malloc(strlen(atmbuf)+1);
  422.     strcpy(valstr,atmbuf);
  423.     confirm();
  424.     cmxprintf("String: %s\n",valstr);
  425.     free(valstr);
  426.     valstr = NULL;
  427.   }
  428. }
  429.  
  430.  
  431.  
  432. /* 
  433.  * break chars for delimited parse
  434.  * only first char break chars are valid delimiters.
  435.  */
  436. static brktab dstbrk = {
  437.   {
  438.     0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x01,
  439.     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
  440.   },
  441.   {
  442.     0x00, 0xac, 0x25, 0x10, 0x00, 0x00, 0x00, 0x01,
  443.     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
  444.   }
  445. };
  446.  
  447. dst(helpflg)
  448. int helpflg;
  449. {
  450.   static fdb dstfdb = { _CMQST, 0, NULL, NULL, NULL, NULL, &dstbrk };
  451.   static char *valstr= NULL;
  452.   
  453.   if (helpflg) {
  454.     cmxprintf("\
  455. Accepts a delimited string.  The final delimiter must match the\n\
  456. first delimiter.\n");
  457.   }
  458.   else {
  459.     noise("string is");
  460.     parse(&dstfdb,&parseval,&used);
  461.     if (valstr) {
  462.       free(valstr);
  463.       valstr = NULL;
  464.     }
  465.     valstr = malloc(strlen(atmbuf)+1);
  466.     strcpy(valstr,atmbuf);
  467.     confirm();
  468.     cmxprintf("String: %s\n",valstr);
  469.     free(valstr);
  470.     valstr = NULL;
  471.   }
  472. }
  473.  
  474. /*
  475.  * parse text.   up to a newline.
  476.  */
  477. text(helpflg)
  478. int helpflg;
  479. {
  480.   static char *valstr;
  481.   static fdb txtfdb = { _CMTXT, 0, NULL, NULL, NULL, NULL, NULL };
  482.   
  483.   if (helpflg) {
  484.     cmxprintf("Accepts an arbitrary line of text\n");
  485.   }
  486.   else {
  487.     noise("of text is");
  488.     parse(&txtfdb,&parseval,&used);
  489.     if (valstr) {
  490.       free(valstr);
  491.       valstr = NULL;
  492.     }
  493.     valstr = malloc(strlen(atmbuf)+1);
  494.     strcpy(valstr,atmbuf);
  495.     confirm();
  496.     cmxprintf("Line: %s\n",valstr);
  497.     free(valstr);
  498.     valstr = NULL;
  499.   }
  500. }
  501.  
  502. /* 
  503.  * parse a word.
  504.  * actually, a field.
  505.  */
  506. word(helpflg)
  507. int helpflg;
  508. {
  509.   static char *valstr= NULL;
  510.   static fdb wrdfdb = { _CMFLD, 0, NULL, NULL, NULL, NULL, NULL };
  511.   
  512.   if (helpflg) {
  513.     cmxprintf("Accepts a word.  Words are delimited by whitespace, or other\n\
  514. characters which are not in the current break mask.\n");
  515.   }
  516.   else {
  517.     noise("of the day is");
  518.     parse(&wrdfdb,&parseval,&used);
  519.     if (valstr) {
  520.       free(valstr);
  521.       valstr = NULL;
  522.     }
  523.     valstr = malloc(strlen(atmbuf)+1);
  524.     strcpy(valstr,atmbuf);
  525.     confirm();
  526.     cmxprintf("Word: %s\n",valstr);
  527.     free(valstr);
  528.     valstr = NULL;
  529.   }
  530. }
  531.  
  532.  
  533. /*
  534.  * parse a token.
  535.  * use the chainfdb call.   this actually modifies the fdbs, so be careful.
  536.  */
  537. tok(helpflg)
  538. int helpflg;
  539. {
  540.   static fdb tok1fdb = { _CMTOK, TOK_WAK, NULL, (pdat) "+", NULL, NULL, NULL };
  541.   static fdb tok2fdb = { _CMTOK, 0, NULL, (pdat) "-", NULL, NULL, NULL };
  542.   static fdb tok3fdb = { _CMTOK, 0, NULL, (pdat) "**#|", NULL, NULL, NULL };
  543.   static fdb tok4fdb = { _CMTOK, 0, NULL, (pdat) "hello^friend", NULL, NULL,
  544.                NULL };
  545.   static char *t;
  546.   fdb *x;
  547.   if (helpflg) {
  548.     cmxprintf("Accepts a token.  Type 'token ?'  to see what the tokens \
  549. are.\n");
  550.   }
  551.   else {
  552.     noise("a dollar please!");        /* for you new yorkers!!! */
  553.     parse(fdbchn(&tok1fdb,&tok2fdb,&tok3fdb,&tok4fdb,NULL),&parseval,&used);
  554.     t = (char *) used->_cmdat;
  555.     confirm();
  556.     cmxprintf("Selected token: %s\n",t);
  557.   }
  558. }
  559.  
  560. /*
  561.  * parse a (possibly wild) username.
  562.  * wild cards are the same as for files, or groups.
  563.  * '*' matches anything.
  564.  * '?' matches any character
  565.  * []  match a range (may have a ^ as the first char, to negate the range).
  566.  * {}  match any of the strings inside (separated by commas).  The strings 
  567.  *     within the {} may contain any wild cards.
  568.  */
  569. user(helpflg) 
  570. int helpflg;
  571. {
  572.                     /* a wild user */
  573.   static fdb usrfdb = { _CMUSR ,USR_WILD, NULL, NULL, NULL, NULL, NULL };
  574.   static struct passwd **p;        /* returns vector of passwd entries */
  575.   struct passwd *p1;
  576.   int i;
  577.  
  578.   if (helpflg) {
  579.     cmxprintf("parses a username\n");
  580.   }
  581.   else {
  582.     noise("username");
  583.     parse(&usrfdb, &parseval, &used);
  584.     p = parseval._pvusr;
  585.     confirm();
  586. #ifndef MSDOS
  587.     for(i = 0,p1 = *p; p1 != NULL; p1 = p[++i]) {
  588.       cmxprintf("%s:%s:%d:%d:%s:%s:%s\n",p1->pw_name, p1->pw_passwd, 
  589.          p1->pw_uid, p1->pw_gid, p1->pw_gecos,p1->pw_dir,p1->pw_shell);
  590.       fflush(stdout);
  591.     }
  592. #endif
  593.   }
  594. }
  595.  
  596. /*
  597.  * parse a possibly wild group name 
  598.  */
  599. groupcmd(helpflg) 
  600. int helpflg;
  601. {
  602.   int i,j;
  603.   static fdb grpfdb = { _CMGRP ,GRP_WILD, NULL, NULL, NULL, NULL, NULL };
  604.   static struct group **g,*g1;        /* returns vector of group entries */
  605.   
  606.   if (helpflg) {
  607.     cmxprintf("parses a wild group name\n");
  608.   }
  609.   else {
  610.     noise("group name");
  611.     parse(&grpfdb, &parseval, &used);
  612.     g = parseval._pvgrp;
  613.     confirm();
  614. #ifndef MSDOS
  615.     for (j = 0, g1 = *g; g1 != NULL; g1 = g[++j]) {
  616.       cmxprintf("%s:%s:%d:",g1->gr_name, g1->gr_passwd, g1->gr_gid);
  617.       for (i = 0; g1->gr_mem[i] != NULL; i++) {
  618.     cmxprintf("%s",g1->gr_mem[i]);
  619.     if (g1->gr_mem[i+1])
  620.       putchar(',');
  621.       }
  622.       cmxprintf("\n"); fflush(stdout);
  623.     }
  624. #endif
  625.   }
  626. }
  627.  
  628. /*
  629.  * parse a wild filename.
  630.  * show the type of file on help.
  631.  */
  632.  
  633. fil(helpflg)
  634. int helpflg;
  635. {
  636.   static filblk fblk;
  637.   static fdb filfdb = { _CMFIL ,FIL_NODIR|FIL_VAL, NULL, (pdat)&fblk, NULL, 
  638.                 NULL, NULL };
  639.   FILE *fp;
  640.   char c;
  641.   char **filelist;
  642.   int i;
  643.   static char *ex[] = { ".txt", NULL };
  644.  
  645.   if (helpflg) {
  646.     cmxprintf("parses a possibly wild filename.\n");
  647.   }
  648.   else {
  649.     fblk.pathv = NULL;
  650.     fblk.exceptionspec = ".*";
  651.     fblk.def_extension = ex;
  652.     noise("to see");
  653.     parse(&filfdb, &parseval, &used);
  654.     filelist = parseval._pvfil;
  655.     confirm();
  656.     for(i = 0; filelist[i] != NULL; i++)
  657.       cmxprintf("%s\n",filelist[i]);
  658.   }
  659. }
  660.  
  661. /*
  662.  * parse a time and/or date.
  663.  */
  664. xtad(helpflg,flags)
  665. int helpflg,flags;
  666. {
  667.   static fdb tadfdb = { _CMTAD, 0, NULL, (pdat) 0, NULL, NULL, NULL };
  668.   static datime dtblk, *dt;
  669.   static char *(dows[]) = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
  670.   static char *(mths[]) = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
  671.       "Aug", "Sep", "Oct", "Nov", "Dec" };
  672.   int tz;
  673.   tzinf *tzi,*dttzinf();
  674.   char *tzname,tzsign;
  675.  
  676.   if (helpflg) {
  677.     cmxprintf("parse a%s%s%s\n", !(flags&DTP_NTI) ? " time" : "",
  678.           !(flags & (DTP_NTI|DTP_NDA)) ? " and" : "",
  679.           !(flags&DTP_NDA) ? " date" : "");
  680.   }
  681.   else {
  682.     tadfdb._cmffl = flags;
  683.     noise("is");
  684.     parse(&tadfdb,&parseval,&used);
  685.     dt = &parseval._pvtad;
  686.     dtblk._dtmon = dt->_dtmon;
  687.     dtblk._dtday = dt->_dtday;
  688.     dtblk._dtyr = dt->_dtyr;
  689.     dtblk._dtdow = dt->_dtdow;
  690.     dtblk._dthr = dt->_dthr;
  691.     dtblk._dtmin = dt->_dtmin;
  692.     dtblk._dtsec = dt->_dtsec;
  693.     dtblk._dttz = dt->_dttz;
  694.     dtblk._dtdst = dt->_dtdst;
  695.     confirm();
  696.     cmxprintf("%s %d-%s-%d ",dows[dtblk._dtdow], dtblk._dtday+1,
  697.         mths[dtblk._dtmon], dtblk._dtyr);
  698.     cmxprintf("%d:%d:%d-",dtblk._dthr, dtblk._dtmin, dtblk._dtsec);
  699.     tz = dtblk._dttz;
  700.     tzi = dttzinf(tz);
  701.     if (tzi == NULL) {
  702.       tzsign = tz < 0 ? '+' : '-';
  703.       if (tz < 0) tz = -tz;
  704.       cmxprintf("GMT%c%d:%d%s\n",tzsign,tz/60,tz%60,
  705.           dtblk._dtdst==0?"":" DST");
  706.     }
  707.     else
  708.       cmxprintf("%s\n",dtblk._dtdst==0?tzi->_tznam:tzi->_tzdnm);
  709.   }
  710. }
  711.     
  712. /*
  713.  * parse a time
  714.  */
  715. tim(helpflg)
  716. int helpflg;
  717. {
  718.   xtad(helpflg,DTP_NDA);
  719. }
  720.  
  721. /*
  722.  * parse a date
  723.  */
  724. dat(helpflg)
  725. int helpflg;
  726. {
  727.   xtad(helpflg,DTP_NTI);
  728. }
  729.  
  730. /*
  731.  * parse time and date
  732.  */
  733. tad(helpflg)
  734. int helpflg;
  735. {
  736.   xtad(helpflg,0);
  737. }
  738.  
  739. /*
  740.  * parse a paragraph
  741.  */
  742. char *paratxt;
  743. int paracont = FALSE;
  744. static fdb parafdb = { _CMPARA, CM_NEOF, NULL, NULL, NULL, NULL, NULL };
  745. para_data pd = { NULL, NULL };
  746. static brktab pipeparabrk = {
  747.   {                    /* just newline is a brk char */
  748.     0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  749.     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  750.   },
  751.   {
  752.     0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  753.     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  754.   }
  755. };
  756.  
  757.  
  758. para(helpflg)
  759. int helpflg;
  760. {
  761.   if (helpflg) {
  762.     cmxprintf("parse a paragraph, using a TEXTI style form of input.\n");
  763.   }
  764.   else {
  765.     static char *tmp=NULL;
  766.  
  767.     if (!(cmcsb._cmflg & CM_ITTY))
  768.     parafdb._cmbrk = &pipeparabrk;
  769.     if (!paracont) {
  770.       noise("of text");
  771.       confirm();
  772.       if (paratxt) free(paratxt);
  773.       paratxt = NULL;
  774.     }
  775.     parafdb._cmdat = (char *)&pd;
  776.     pd.buf = paratxt; 
  777.     cmcls();                /* clear the screen */
  778.     cmxprintf(" Message (End with CTRL/D\n\
  779.   Use CTRL/B to insert a file, CTRL/E to enter editor, CTRL/K to redisplay\n\
  780.   message, CTRL/L to clear screen and redisplay, CTRL/N to abort.):\n");
  781.     parse(¶fdb,&parseval,&used);
  782.     paratxt = parseval._pvpara;
  783.     if (paratxt == NULL) {
  784.       cmxprintf("Aborted!\n");
  785.     }
  786.     else {
  787.       if (tmp) 
  788.     free(tmp);
  789.       tmp = (char *)malloc(strlen(paratxt)+1);
  790.       strcpy(tmp,paratxt);
  791.       paratxt = tmp;
  792.       cmxprintf("Read %d chars.\n",strlen(paratxt)); fflush(stdout);
  793. /*
  794.  * don't free tmp, so that the continue command can still have a handle on 
  795.  * the buffer.  It will be free()ed the next time this routine is executed.
  796.  */
  797.     }
  798.     fflush(stdout);
  799.   }
  800.   paracont = FALSE;
  801. }
  802.  
  803. /*
  804.  * continue parsing a paragraph
  805.  */
  806. contpara(helpflg) {
  807.   if (helpflg) {
  808.     cmxprintf("continue editing the paragraph previously typed paragraph\n");
  809.   }
  810.   else {
  811.     noise("paragraph");
  812.     confirm();
  813.     paracont = TRUE;
  814.     para(helpflg);
  815.   }
  816. }
  817.  
  818. /*
  819.  * take commands from some other FILE *
  820.  */
  821. take() {
  822.   cmtake(toplevel);
  823. }
  824.  
  825. /*
  826.  * change the characters that define a comment
  827.  * the defaults are ";", and "!" to "!".
  828.  */
  829. comment(helpflg) {
  830.   static fdb qstfdb = { _CMQST, 0, NULL, NULL, NULL, NULL, NULL };
  831.   static char xtoeol[20], xbeg[20], xend[20];
  832.   static char toeol[20], beg[20], end[20];
  833.   if (helpflg) {
  834.     cmxprintf("set the CCMD comment characters");
  835.   }
  836.   else {
  837.     noise("To eol");
  838.     parse(&qstfdb,&parseval,&used);
  839.     strcpy(xtoeol,atmbuf);
  840.     noise("start");
  841.     parse(&qstfdb,&parseval,&used);
  842.     strcpy(xbeg,atmbuf);
  843.     noise("end");
  844.     parse(&qstfdb,&parseval,&used);
  845.     strcpy(xend,atmbuf);
  846.     confirm();
  847.     strcpy(toeol,xtoeol);
  848.     strcpy(beg,xbeg);
  849.     strcpy(end,xend);
  850.     cmcsb._cmntb = toeol;
  851.     cmcsb._cmnts = beg;
  852.     cmcsb._cmnte = end;
  853.   }
  854. }
  855.  
  856. /*
  857.  * parse a password  -- don't echo.
  858.  */
  859. password(helpflg) int helpflg; {
  860.   char *cmpasswd();
  861.   char *passwd;
  862.   if (helpflg) {
  863.     cmxprintf("parse for a password.   Don't echo.\n");
  864.   }
  865.   else {
  866.     confirm();
  867.     passwd = cmpasswd("Password:");
  868.     if (passwd == NULL) {
  869.       cmxerr("?Invalid characters in password");
  870.     }
  871.     else cmxprintf("Password: %s\n", passwd);
  872.   }
  873. }
  874.  
  875. /*
  876.  * match a string via an fdb chain.
  877.  * currently used to expand "*" as a filename.
  878.  */
  879.  
  880. domatch(helpflg) int helpflg; {
  881.   static fdb matfdb = { _CMTAD ,0, NULL, NULL, NULL, NULL, NULL };
  882.   static char *matstr = "17-May-86 13:52:06-EDT"; 
  883.   char **p,*p1;
  884.   int i;
  885.   if (helpflg) {
  886.     cmxprintf("\
  887. match a filename against the string '*'.  This tests ccmd's ability to \n\
  888. match strings in memory rather than having to rely on user input.\n");
  889.   }
  890.   else {
  891.     int ret, parselen;
  892.     static fdb tadfdb = { _CMTAD, 0, NULL, (pdat) 0, NULL, NULL, NULL };
  893.     static datime dtblk, *dt;
  894.     static char *(dows[]) = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
  895.     static char *(mths[]) = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
  896.                   "Aug", "Sep", "Oct", "Nov", "Dec" };
  897.     int tz;
  898.     tzinf *tzi,*dttzinf();
  899.     char *tzname,tzsign;
  900.     noise(matstr);
  901.     confirm();
  902.     ret = match(matstr,strlen(matstr),&matfdb, &parseval, &used, &parselen);
  903.     dt = &parseval._pvtad;
  904.     dtblk._dtmon = dt->_dtmon;
  905.     dtblk._dtday = dt->_dtday;
  906.     dtblk._dtyr = dt->_dtyr;
  907.     dtblk._dtdow = dt->_dtdow;
  908.     dtblk._dthr = dt->_dthr;
  909.     dtblk._dtmin = dt->_dtmin;
  910.     dtblk._dtsec = dt->_dtsec;
  911.     dtblk._dttz = dt->_dttz;
  912.     dtblk._dtdst = dt->_dtdst;
  913.     if (ret == CMxOK) {
  914.     cmxprintf("%s %d-%s-%d ",dows[dtblk._dtdow], dtblk._dtday+1,
  915.           mths[dtblk._dtmon], dtblk._dtyr);
  916.     cmxprintf("%d:%d:%d-",dtblk._dthr, dtblk._dtmin, dtblk._dtsec);
  917.     tz = dtblk._dttz;
  918.     tzi = dttzinf(tz);
  919.     if (tzi == NULL) {
  920.         tzsign = tz < 0 ? '+' : '-';
  921.         if (tz < 0) tz = -tz;
  922.         cmxprintf("GMT%c%d:%d%s\n",tzsign,tz/60,tz%60,
  923.               dtblk._dtdst==0?"":" DST");
  924.     }
  925.     else
  926.         cmxprintf("%s\n",dtblk._dtdst==0?tzi->_tznam:tzi->_tzdnm);
  927.     }
  928.     else {
  929.       cmxeprintf("domatch: ");
  930.       cmperr(ret);
  931.     }
  932.   }
  933. }
  934.  
  935. /*
  936.  * give help.
  937.  */
  938. help(helpflg) {
  939.   static fdb cmdfdb = { _CMKEY, 0, NULL, (pdat) &(cmdtab), "Command, ", 
  940.               NULL, NULL };
  941.   static fdb hlpfdb = { _CMCFM, 0, &cmdfdb, NULL, NULL, NULL, NULL };
  942.   
  943.   if (helpflg) {
  944.     cmxprintf("The help command gives help.\n");
  945.   }
  946.   else {
  947.     noise("me with");
  948.     parse(&hlpfdb,&parseval,&used);    /* parse a command */
  949.     if (used == &hlpfdb) {
  950.       cmxprintf("\
  951. This is a program to test and display some of the capabilities of the CCMD\n\
  952. package.  CCMD is a user interface package which expands upon the COMND% \n\
  953. JSYS used as a user interface by DEC's TOPS-20.  CCMD is a library, written\n\
  954. to be portable to many machines.  Currently, it runs under 4.2 BSD UNIX, and\n\
  955. MS-DOS, and will hopefully soon run on a diverse set of machines.  It is\n\
  956. hoped that this will allow a homogenious user interface to evolve on many\n\
  957. different machines.  If you are unfamiliar with the COMND interface, try the\n\
  958. '?' key.  Also try typing part of a command, and then hitting escape.\n");
  959.     }
  960.     else {
  961.       int which = parseval._pvkey;
  962.       confirm();
  963.       dohelp(which);            /* help on a subject. */
  964.     }
  965.   }
  966. }
  967.  
  968. /*
  969.  * change the character to use as a beginning switch delimiter.
  970.  */
  971. character(helpflg) {
  972.   static fdb swifdb = { _CMCHAR, 0, NULL, NULL, NULL, NULL, NULL };
  973.  
  974.   if (helpflg) {
  975.     cmxprintf("set the character that switches switch on\n");
  976.   }
  977.   else {
  978.     char c;
  979.     extern char cmswbeg;
  980.  
  981.     noise("to switch on");
  982.     parse(&swifdb,&parseval,&used);
  983.     c = parseval._pvchr;
  984.     confirm();
  985.     cmswbeg = c;
  986.   }
  987. }
  988.  
  989. shell(helpflg) int helpflg; {
  990.   static fdb txtfdb = { _CMTXT, 0, NULL, NULL, NULL, NULL, NULL };
  991.   char buf[100];
  992.   if (!helpflg) {
  993.     noise("escape");
  994.     parse(&txtfdb, &parseval, &used);
  995.     strcpy(buf, "$SHELL");
  996.     if (strlen(atmbuf) > 0) {
  997.       strcat(buf," -c \'");
  998.       strcat(buf, atmbuf);
  999.       strcat(buf, "\'");
  1000.     }
  1001.     confirm();
  1002.     cmsystem(buf);
  1003.   }
  1004.   else {
  1005.     cmxprintf("fork up a shell\n");
  1006.   }
  1007. }
  1008.  
  1009. nil(helpflg) {
  1010.     static fdb usrfdb = { _CMUSR ,USR_WILD|USR_UPDONLY|USR_NOUPD };
  1011.     int len = 0;
  1012.     if (helpflg) {
  1013.     cmxprintf("do nothing\n");
  1014.     }
  1015.     else {
  1016.         noise("empty");
  1017.     match("", 0, &usrfdb, &parseval, &used, &len);
  1018.     confirm();
  1019.     cmxprintf("t\n");
  1020.     }
  1021. }
  1022.  
  1023. history(helpflg) {
  1024.     static fdb numfdb = { _CMNUM, 0, NULL, (pdat) 10, NULL, NULL, NULL };    
  1025.     int n;
  1026.  
  1027.     parse(&numfdb, &parseval, &used);
  1028.     n = parseval._pvint;
  1029.     confirm();
  1030.     cmhst(n);
  1031.     printf("setting history to %d\n",n);
  1032. }
  1033.